﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Transactions;
using MyBasicLib.Utilities;

namespace MyBasicLib.Data.ORMLiteNew
{
    public sealed  class DataContext : IDisposable
    {

        System.Transactions.CommittableTransaction tran { get { return System.Transactions.Transaction.Current as CommittableTransaction; } }
        int affected;
        #region 构造函数

        public DataContext()
            : this(IOC.DefaultContainer.Resolve<IDbQueryProvider>())
        {
        }
        public DataContext(string connectionString)
            : this(IOC.DefaultContainer.Resolve<IDbQueryProvider>(), connectionString)
        {
        }
        public DataContext(IDbQueryProvider provider, string connectionString = null)
        {
            Provider = provider;
            if (!string.IsNullOrEmpty(connectionString))
            {
                Provider.Executor.ConnectionString = connectionString;
            }
        }

        #endregion
        public IDbQueryProvider Provider { get; set; }
        bool IsTransactionValid()
        {
            return tran != null && tran.TransactionInformation.Status == TransactionStatus.Active;
        }
        System.Transactions.CommittableTransaction GetNewTransaction()
        {
            if (!IsTransactionValid())
            {
                var tranOption = new TransactionOptions();
                tranOption.IsolationLevel = IsolationLevel.ReadUncommitted;
                tranOption.Timeout = TimeSpan.FromMinutes(3);
                System.Transactions.Transaction.Current = new System.Transactions.CommittableTransaction(tranOption);
            }
            return tran;
        }

        /// <summary>
        /// 新增
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value">实体</param>
        public int Insert<T>(T value) //where T : class
        {
            GetNewTransaction();
            IDbQueryable<T> iq = GetTable<T>();
            iq.DbExpressionList.Add(new DbExpression(DbExpressionType.Insert, System.Linq.Expressions.Expression.Constant(value)));
            var thisAffected = Provider.ExecuteNonQuery(iq);
            affected += thisAffected;
            return thisAffected;
        }
        /// <summary>
        /// 新增
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value">实体列表</param>
        public int Insert<T>(IEnumerable<T> values) //where T : class
        {
            var thisAffected = 0;
            foreach (T value in values)
            {
                thisAffected += Insert(value);
            }
            return thisAffected;
        }
        /// <summary>
        /// 新增从查询里
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value">查询</param>
        public int Insert<T>(IDbQueryable<T> query) //where T : class
        {
            GetNewTransaction();

            IDbQueryable<T> iq = query;
            iq.DbExpressionList.Add(new DbExpression(DbExpressionType.Insert));
            var thisAffected = Provider.ExecuteNonQuery(iq);
            affected += thisAffected;
            return thisAffected;
        }

        /// <summary>
        /// 删除
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="predicate">条件</param>
        public int Delete<T>(Expression<Func<T, bool>> predicate)
        {
            return Delete<T>(p => p.Where(predicate));
        }

        private int Delete<T>(Func<IDbQueryable<T>, IDbQueryable<T>> predicate)
        {
            IDbQueryable<T> iq = GetTable<T>();
            IDbQueryable<T> newIq = predicate(iq);
            return Delete(newIq);
        }
        /// <summary>
        /// 删除从查询里
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="query">查询</param>
        public int Delete<T>(IDbQueryable<T> query)
        {
            GetNewTransaction();

            query.DbExpressionList.Add(new DbExpression(DbExpressionType.Delete));
            var thisAffected = Provider.ExecuteNonQuery(query);
            affected += thisAffected;
            return thisAffected;
        }
        /// <summary>
        /// 更新
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="newValue">实体</param>
        public int Update<T>(T newValue)
        {
            GetNewTransaction();

            IDbQueryable<T> query = GetTable<T>();
            query.DbExpressionList.Add(new DbExpression(DbExpressionType.Update, Expression.Constant(newValue)));
            var thisAffected = Provider.ExecuteNonQuery(query);
            affected += thisAffected;
            return thisAffected;
        }

        /// <summary>
        /// 更新
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="action">要更新的字段</param>
        /// <param name="predicate">条件</param>
        public int Update<T>(Expression<Func<T, T>> action, Expression<Func<T, bool>> predicate)
        {
            return Update(action, p => p.Where(predicate));
        }

        private int Update<T>(Expression<Func<T, T>> action, Func<IDbQueryable<T>, IDbQueryable<T>> predicate)
        {
            IDbQueryable<T> iq = GetTable<T>();
            IDbQueryable<T> newIq = predicate(iq);
            return Update(action, newIq);
        }
        /// <summary>
        /// 更新从查询里
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="action">要更新的字段</param>
        /// <param name="query">查询</param>
        public int Update<T>(Expression<Func<T, T>> action, IDbQueryable<T> query)
        {
            GetNewTransaction();

            query.DbExpressionList.Add(new DbExpression(DbExpressionType.Update, action));
            var thisAffected = Provider.ExecuteNonQuery(query);
            affected += thisAffected;
            return thisAffected;

        }
        /// <summary>
        /// 查询某个表
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public IDbQueryable<T> GetTable<T>()
        {
            if (tran != null && !IsTransactionValid())
                System.Transactions.Transaction.Current = null;
            var q = new DbQueryable<T>() { Provider = Provider };
            q.DbExpressionList = new List<DbExpression>() { new DbExpression( DbExpressionType.GetTable, 
              System.Linq.Expressions.Expression.Constant(typeof(T)) ) };
            return q;
        }
       
         

        /// <summary>
        /// 提交事务
        /// </summary>
        /// <returns>影响记录数量</returns>
        public int SubmitChanges()
        {
            if (tran == null)
                return 0;
            try
            {
                tran.Commit();
            }
            catch
            {
                tran.Rollback();
                return 0;
            }
            finally
            {
                Dispose(); 
            }
            return affected;
        }
        /// <summary>
        /// 取消事务
        /// </summary>
        public void DiscardChanges()
        {
            if (tran == null)
                return;
            try
            {
                tran.Rollback();
            }
            finally
            {
                Dispose();
            }
        }

        Boolean disposed;
        // Implement IDisposable.
        // Do not make this method virtual.
        // A derived class should not be able to override this method.
        public void Dispose()
        {
            Dispose(true);
            // This object will be cleaned up by the Dispose method.
            // Therefore, you should call GC.SupressFinalize to
            // take this object off the finalization queue
            // and prevent finalization code for this object
            // from executing a second time.
            GC.SuppressFinalize(this);
        }
        // Dispose(bool disposing) executes in two distinct scenarios.
        // If disposing equals true, the method has been called directly
        // or indirectly by a user's code. Managed and unmanaged resources
        // can be disposed.
        // If disposing equals false, the method has been called by the
        // runtime from inside the finalizer and you should not reference
        // other objects. Only unmanaged resources can be disposed.
        private   void Dispose(bool disposing)
        {
            // Check to see if Dispose has already been called.
            if (!this.disposed)
            {
                // If disposing equals true, dispose all managed
                // and unmanaged resources.
                if (disposing)
                {
                    // Dispose managed resources.
                    if (tran != null)
                    {
                        tran.Dispose();
                        System.Transactions.Transaction.Current = null;
                    }
                }

                // Call the appropriate methods to clean up
                // unmanaged resources here.
                // If disposing is false,
                // only the following code is executed.             

                // Note disposing has been done.
                //disposed = true;//todo 多次disposed问题
            }
        }

        ~DataContext()
        {
            // Do not re-create Dispose clean-up code here.
            // Calling Dispose(false) is optimal in terms of
            // readability and maintainability.
            Dispose(false);
        } 
    }
}